#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include "user_threads.h"
#include "common.h"

int fibonacci(unsigned int i) {
	if (i<2) {
		return i;
	}
	return fibonacci(i-2)+fibonacci(i-1);
}

#include <stdio.h>

#define LOOPS 3
#define FIB 30

#define CHAIN_LENGTH 6

uthread_id threads[CHAIN_LENGTH];

int findSelfInArr() {
	int i;
	for (i=0; i<CHAIN_LENGTH; ++i) {
		if (uthread_self()==threads[i]) {
			return i;
		}
	}
	return -1;
}

int thread1(void * arg)
{
	char c[15];
	int k;
    int i;
    for (i = 0; i < LOOPS/2; ++i) {
//        printf("uthread #%d\n", uthread_self());
		//write(1,"uthread1\n",9);
		fibonacci(FIB);
    }
	int j = findSelfInArr();
	if (j<CHAIN_LENGTH-1) {
		k=sprintf(c,"attempting to join %d\n",threads[j+1]);
		write(1,c,k);
		uthread_join(threads[j+1],NULL);
	}
    for (i = 0; i < LOOPS*2; ++i) {
//        printf("uthread #%d\n", uthread_self());
		
		//k=sprintf(c,"uthread%d\n",uthread_self());
		//write(1,c,k);
		fibonacci(FIB);
    }
    printf("uthread #%d finished\n", uthread_self());
    return 66;
}

int killerThread1(void* arg) {
    int i;
	char c[30];
	int k;
    for (i = 0; i < LOOPS*1; ++i) {
//        printf("uthread #%d\n", uthread_self());
		k=sprintf(c,"Killer Thread\n");
		write(1,c,k);
		fibonacci(FIB);
    }
	
	k=sprintf(c,"Killer Thread Killing\n");	
	write(1,c,k);
	for (i=0; i<CHAIN_LENGTH-1; ++i) {
		uthread_cancel(threads[i]);
	}
	return 6;
}

int main() {
	uthread_init();
	int i;
	int killerRes, killedRes;
	uthread_id killer;
	for (i=0; i<CHAIN_LENGTH; ++i) {
		threads[i]=uthread_create(&thread1, NULL, UTHREAD_MIN_PRIO);
	}
	killer = uthread_create(&killerThread1, NULL, UTHREAD_MIN_PRIO);
	assert(uthread_join(killer, &killerRes)==UTHREAD_SUCCESS);
	
	
	uthread_yield();
	printf("Thread 0 joining top of chain\n");
	assert(uthread_join(threads[0],&killedRes)==UTHREAD_SUCCESS);
	printf("Thread 0 returned from join. Should be after last thread in chain finished.\n");
	assert(uthread_join(threads[0],&killedRes)==UTHREAD_INVALID);

	char r[20];
	int rl = sprintf(r,"Killer Returned %d\n",killerRes);
	write(1,r,rl);
	rl = sprintf(r,"Killed Returned %d\n",killedRes);
	write(1,r,rl);
	assert(uthread_join(threads[0],&killedRes)==UTHREAD_INVALID);
	
	return 0;
}
